package cool.leeson.library.service.receive;

import com.aliyuncs.utils.StringUtils;
import cool.leeson.library.dao.ReceiveItemDao;
import cool.leeson.library.dao.UserCreditDao;
import cool.leeson.library.entity.library.Library;
import cool.leeson.library.entity.receive.ReceiveItem;
import cool.leeson.library.entity.receive.ReceiveItemPost;
import cool.leeson.library.entity.receive.ReceiveItemResponse;
import cool.leeson.library.entity.tools.RedisTool;
import cool.leeson.library.entity.user.UserCredit;
import cool.leeson.library.entity.user.UserOnline;
import cool.leeson.library.exceptions.MyException;
import cool.leeson.library.service.library.LibraryRoomService;
import cool.leeson.library.service.library.LibrarySeatService;
import cool.leeson.library.service.library.LibraryService;
import cool.leeson.library.service.user.UserInfoService;
import cool.leeson.library.service.user.UserOnlineService;
import cool.leeson.library.util.ResMap;
import cool.leeson.library.util.StatusCodeGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * (ReceiveOrder)表服务实现类
 *
 * @author Leeson0202
 * @since 2023-03-17 16:43:01
 */
@Service("receiveService")
@Slf4j
@Transactional
public class ReceiveService {
    @Resource
    private ReceiveItemDao receiveItemDao;
    @Resource
    private UserCreditDao userCreditDao;
    @Resource
    private UserInfoService userInfoService;

    @Resource
    private UserOnlineService userOnlineService;
    @Resource
    private LibraryService libraryService;
    @Resource
    private LibraryRoomService libraryRoomService;
    @Resource
    private LibrarySeatService librarySeatService;
    @Resource
    private RedisTool redisTool;


    /**
     * 提交预约订单
     *
     * @param receiveItemPosts 数据
     * @return 实体
     */
    @Transactional
    public Map<String, Object> receive(List<ReceiveItemPost> receiveItemPosts, String userId) throws MyException {
        if (receiveItemPosts == null || receiveItemPosts.size() == 0)
            throw new MyException(MyException.STATUS.requestErr);
        Date now = new Date();
        // 构建 receiveItems
        List<ReceiveItem> receiveItems = new ArrayList<>();
        for (ReceiveItemPost receiveItemPost : receiveItemPosts) {
            int today = receiveItemPost.getToday() ? 0 : 1;
            // 查询用户在该时段是否预约
            String userKey = String.format(RedisTool.FormatKey.RECEIVE.toString(), userId, now.getDate() + today, receiveItemPost.getTimeIdx());
            String rUserRecord = redisTool.get(userKey);
            if (!StringUtils.isEmpty(rUserRecord) && "true".equals(rUserRecord)) {
                // 说明有数据
                return ResMap.err("您已经预约了该时段");
            }
            // 查询座位是否占用
            String seatKey = String.format(RedisTool.FormatKey.RECEIVE.toString(), receiveItemPost.getSeatId(), now.getDate() + today, receiveItemPost.getTimeIdx());
            String rSeatRecord = redisTool.get(seatKey);
            if (!StringUtils.isEmpty(rSeatRecord) && "true".equals(rSeatRecord)) {
                // 说明有数据
                return ResMap.err("座位已占座，请刷新");
            }
            ReceiveItem receiveItem = new ReceiveItem();
            // 构建 receiveItem
            receiveItem.setReceiveId(UUID.randomUUID().toString());
            receiveItem.setUserId( userId);
            receiveItem.setCheckStatus(0); //未入座
            receiveItem.setLibraryId(receiveItemPost.getLibraryId());
            receiveItem.setRoomId(receiveItemPost.getRoomId());
            receiveItem.setSeatId(receiveItemPost.getSeatId());
            receiveItem.setReceiveDate(new Date(now.getYear(), now.getMonth(), now.getDate() + today));
            receiveItem.setCheckInCode(StatusCodeGenerator.generateStatusCode());
            receiveItem.setCheckOutCode(StatusCodeGenerator.generateStatusCode());
            receiveItem.setTime(now);
            receiveItem.setTimeIdx(receiveItemPost.getTimeIdx());


            receiveItems.add(receiveItem);
        }
        // 插入 items
        if (this.receiveItemDao.insertBatch(receiveItems) < receiveItems.size()) {
            throw new MyException(MyException.STATUS.err);
        }
        // 座位和用户信息，插入redis
        for (ReceiveItemPost receiveItemPost : receiveItemPosts) {
            int today = receiveItemPost.getToday() ? 0 : 1;
            String seatKey = String.format(RedisTool.FormatKey.RECEIVE.toString(), receiveItemPost.getSeatId(), now.getDate() + today, receiveItemPost.getTimeIdx());
            String userKey = String.format(RedisTool.FormatKey.RECEIVE.toString(), userId, now.getDate() + today, receiveItemPost.getTimeIdx());
            // 剩下的时间
            long milliSecondsLeftToday = 86400000 - DateUtils.getFragmentInMilliseconds(Calendar.getInstance(), Calendar.DATE);
            milliSecondsLeftToday = receiveItemPost.getToday() ? milliSecondsLeftToday : milliSecondsLeftToday + 86400000;
            // 插入
            redisTool.set(seatKey, true, milliSecondsLeftToday, TimeUnit.MILLISECONDS);
            redisTool.set(userKey, true, milliSecondsLeftToday, TimeUnit.MILLISECONDS);
        }
        return new ResMap().putCode(200).putMsg("预约成功").build();
    }

    /**
     * 取消预约
     *
     * @param userId    用户Id
     * @param receiveId 预约Id
     * @return 实体
     * @throws MyException w
     */
    public Map<String, Object> cancel(String userId, String receiveId) throws MyException {
        if (StringUtils.isEmpty(receiveId)) return ResMap.err("请上传receiveId");
        // 查询receive
        ReceiveItem receiveItem = this.receiveItemDao.queryById(receiveId);
        if (receiveItem == null) {
            throw new MyException(MyException.STATUS.requestErr);
        }
        // 获取取消的时间
        Date receiveDate = receiveItem.getReceiveDate();
        Integer timeIdx = receiveItem.getTimeIdx();
        Date now = new Date();
        int idx = (now.getHours() - 8) / 2;
        Date date = new Date(now.getYear(), now.getMonth(), now.getDate());
        // 判断时间是否相同
        if (receiveDate.equals(date) && idx == timeIdx) {
            // 判断用户是否在线
            Map<String, Object> map = userOnlineService.queryById(userId);
            UserOnline userOnline = (UserOnline) map.get("data");
            Integer online = userOnline.getOnline();
            if (online == 1) {
                return ResMap.err("请离座扫码");
            }
            userOnline.setOnline(0);
            userOnlineService.update(userOnline);
        }
        if (receiveItemDao.deleteById(receiveId) != 1) {
            throw new MyException(MyException.STATUS.err);
        }
        String userKey = String.format(RedisTool.FormatKey.RECEIVE.toString(), userId, receiveDate.getDate(), timeIdx);
        String seatKey = String.format(RedisTool.FormatKey.RECEIVE.toString(), receiveItem.getSeatId(), receiveDate.getDate(), timeIdx);
        redisTool.set(userKey, "false", 1, TimeUnit.SECONDS);
        redisTool.set(seatKey, "false", 1, TimeUnit.SECONDS);
        return ResMap.ok("取消成功");
    }

    /**
     * 获取用户的行程计划
     *
     * @param userId 用户Id
     * @return 实体
     * @throws MyException ms
     */
    public Map<String, Object> schedule(String userId) throws MyException {
        log.info(userId + " 正在获取行程计划");
        // 先查询所有
        List<ReceiveItemResponse> items = (List<ReceiveItemResponse>) this.queryAll(userId).get("data");
        Date now = new Date();
        // 时间Idx
        int timeIndex;
        int today = 0;
        if (now.getHours() < 10) {
            // 确保两个小时的预约时间
            timeIndex = 0;
        } else if (now.getHours() >= 22) {
            // 第二天
            timeIndex = 0;
            today = 1;
        } else {
            timeIndex = (now.getHours() - 8) / 2;
        }
        // 要展现的时间 年月日
        Date date = new Date(now.getYear(), now.getMonth(), now.getDate() + today);
        List<ReceiveItemResponse> responseItems = new ArrayList<>();
        for (ReceiveItemResponse item : items) {
            // 比这个时间早，直接继续 日期早或index小
            if (item.getReceiveDate().before(date) || (item.getReceiveDate().equals(date) && item.getTimeIdx() < timeIndex)) {
                continue;
            }
            responseItems.add(item);
        }
        // 排序
        Collections.sort(responseItems);
        return ResMap.ok(responseItems);

    }

    /**
     * 获取用户的所有预约
     *
     * @param userId 用户Id
     * @return 实体
     * @throws MyException ms
     */
    public Map<String, Object> queryAll(String userId) throws MyException {
        log.info(userId + "查询所有记录");
        // 先根据userId查询所有预约订单
        List<ReceiveItem> items = this.receiveItemDao.queryByUserId(userId);
        List<ReceiveItemResponse> responseItems = new ArrayList<>();
        for (ReceiveItem item : items) {
            // 构建
            ReceiveItemResponse responseItem = new ReceiveItemResponse(item);
            // date
            String ddate = (item.getReceiveDate().getMonth() + 1) + "月" + item.getReceiveDate().getDate() + "日";
            // 获取名字
            System.out.println("libraryId is " + item.getLibraryId());
            // 确保预约的图书馆还存在，如果图书馆已经删除，则不会添加预约信息
            Library library_get = this.libraryService.queryInfo(item.getLibraryId());
            if (library_get != null) {
                String libraryName = library_get.getName();
                String roomName = this.libraryRoomService.queryInfo(item.getRoomId()).getName();
                String seatName = this.librarySeatService.queryInfo(item.getSeatId()).getName();
                responseItem.setDate(ddate);
                responseItem.setLibraryName(libraryName);
                responseItem.setRoomName(roomName);
                responseItem.setSeatName(seatName);
                responseItems.add(responseItem);
            }
//            String libraryName = this.libraryService.queryInfo(item.getLibraryId()).getName();// invoke null
        }
        // 排序
        Collections.sort(responseItems);
        return ResMap.ok(responseItems);
    }


    /**
     * 通过房间Id获取全部预约
     *
     * @param receiveItem 实体
     */

    public Map<String, Object> queryAllByLimit(ReceiveItem receiveItem, Integer page, Integer size) throws MyException {
        PageRequest pageRequest = PageRequest.of(page, size, Sort.by("receiveDate").descending());
        long total = this.receiveItemDao.count(receiveItem);
        System.out.println("count get is "+ total);
        List<ReceiveItem> receiveItems = this.receiveItemDao.queryAllByLimit(receiveItem, pageRequest);
        List<ReceiveItemResponse> receiveItemResponses = new ArrayList<>();
        for (ReceiveItem item : receiveItems) {
            ReceiveItemResponse receiveItemResponse = new ReceiveItemResponse(item);
            // 名字
            receiveItemResponse.setNickName(userInfoService.queryById(item.getUserId()).getNickName());
            // 状态
//            UserOnline userOnline = (UserOnline) userOnlineService.queryById(item.getUserId()).get("data");
//            receiveItemResponse.setOnline(userOnline.getOnline());

            // 图书馆
            receiveItemResponse.setLibraryName(this.libraryService.queryInfo(item.getLibraryId()).getName());
            // 图书室
            receiveItemResponse.setRoomName(this.libraryRoomService.queryInfo(item.getRoomId()).getName());
            // 椅子
            receiveItemResponse.setSeatName(this.librarySeatService.queryInfo(item.getSeatId()).getName());
            // 时间
            receiveItemResponse.setDate(item.getReceiveDate().getMonth() + 1 + "月" + item.getReceiveDate().getDate() + "日");
            // 签到码
            receiveItemResponse.setCheckInCode(item.getCheckInCode());
            receiveItemResponse.setCheckOutCode(item.getCheckOutCode());
            receiveItemResponses.add(receiveItemResponse);
        }
        Collections.sort(receiveItemResponses);
        Collections.reverse(receiveItemResponses);
        PageImpl<ReceiveItemResponse> res = new PageImpl<>(receiveItemResponses, pageRequest, total);
        return ResMap.ok(res);
    }

    /**
     * 签到
     */
    public Map<String, Object> checkIn(String code, String receiveId, String userId) {
        Date now = new Date();
        // 签到逻辑需要接收预约ID和用户输入的动态码，
        Calendar calendar = Calendar.getInstance();
        ReceiveItem receiveItem = receiveItemDao.queryById(receiveId);
        if (receiveItem == null) {
            return ResMap.err("没有该预约订单！");
        }
        Date startTime = receiveItem.getReceiveDate();
        Integer timeIdx = receiveItem.getTimeIdx();
        calendar.setTime(startTime);
        calendar.set(Calendar.HOUR_OF_DAY, 2 * timeIdx + 8); // 计算小时数
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        startTime = calendar.getTime(); // 获取正确的 startTime

        calendar.setTime(startTime);
        calendar.add(Calendar.MINUTE, -10);
        Date tenBefore = calendar.getTime(); // 前十分钟
        System.out.println("start time " + startTime + ", now is " + now);
        // 1、如果在预约开始时间的前十分钟内，动态码验证成功
        // 2、如果预约开始时间之后，显示迟到，扣除信用分 -5
//        if(now.before(tenBefore)) {
//            System.out.println("start time " + startTime + ", now is " + now);
//            return ResMap.ok("未到签到时间");
//        }
        if (!code.equals(receiveItem.getCheckInCode())) {
            return ResMap.ok("签到码错误！");
        }
//        if (now.after(startTime)) {
//            // 迟到
//            UserCredit userCredit = userCreditDao.queryByUserId(userId);
//            // 不存在userCrtedit不存在，创建用户时创建
//            if (userCredit == null) {
//                UserCredit userCredit_create = new UserCredit();
//                userCredit_create.setUserCredit(90);
//                userCredit_create.setUserId(userId);
//                userCredit_create.setId(UUID.randomUUID().toString());
//                userCreditDao.insert(userCredit_create);
//                return ResMap.ok("迟到！扣除信用分10分，目前信用分："+ userCredit_create.getUserCredit());
//            }
//            userCredit.setUserCredit(userCredit.getUserCredit()-10); // 扣除
//            userCreditDao.update(userCredit);
//            return ResMap.ok("迟到！扣除信用分10分，目前信用分："+ userCredit.getUserCredit());
//        }
        receiveItem.setCheckStatus(1);// 在座状态
        // 签到成功，生成签退码
        receiveItem.setCheckOutCode(StatusCodeGenerator.generateStatusCode());
        receiveItemDao.update(receiveItem);
        return ResMap.ok("签到成功！");
    }

    public Map<String, Object> checkout(String code, String receiveId, String userId) {
        // 签退逻辑
        // 预约结束前十分钟进行签退
        Date now = new Date();
        Calendar calendar = Calendar.getInstance();
        ReceiveItem receiveItem = receiveItemDao.queryById(receiveId);
        if (receiveItem == null) {
            return ResMap.err("没有该预约订单！");
        }
        Date endTime = receiveItem.getReceiveDate();
        Integer timeIdx = receiveItem.getTimeIdx();
        calendar.setTime(endTime);
        calendar.set(Calendar.HOUR_OF_DAY, 2 * timeIdx + 10);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        endTime = calendar.getTime(); // 获取正确的 endTime

        // 获取前十分钟时间
        calendar.setTime(endTime);
        calendar.add(Calendar.MINUTE, -10);
        Date tenBefore = calendar.getTime(); // 前十分钟
        System.out.println("end time " + endTime + ", now is " + now);

        // 由于用户只能看到当前时间之前的预约信息
        // 所以不会出现当前时间超过预约结束时间的情况
        // 这里由前端判断，如果超过预约结束时间，可以自动调用此接口，从而触发惩罚措施
        // 签退失败
        if (now.after(endTime)) {
            UserCredit userCredit = userCreditDao.queryByUserId(userId);
            // 不存在userCrtedit不存在，创建用户时创建
            userCredit.setUserCredit(userCredit.getUserCredit()-10); // 扣除
            userCreditDao.update(userCredit);
            return ResMap.ok("签退失败！扣除信用分10分，目前信用分："+ userCredit.getUserCredit());
        }
        if(now.before(tenBefore)) {
            System.out.println("start time " + endTime + ", now is " + now);
            return ResMap.ok("未到签退时间");
        }
        if (!code.equals(receiveItem.getCheckOutCode())) {
            return ResMap.ok("签退码错误！");
        }

        receiveItem.setCheckStatus(2);// 在座状态
        receiveItemDao.update(receiveItem);
        return ResMap.ok("签退成功！");
    }

}
